package 集合;

import java.util.Collection;

//EnumSet 是一个专为枚举设计的集合类，EnumSet中的所有元素都必须是指定枚举类型的枚举值，该枚举类型在创建EnumSet时显式或隐式地指定。
//
//1.EnumSet的集合元素也是有序的，EnumSet以枚举值在Enum类内的定义顺序来决定集合元素的顺序。
//2.EnumSet在内部以位向量的形式存储，这种存储形式非常紧凑、高效,因此EnumSet对象占用内存很小，而且运行效率很好。尤其是进行批量操
//       作（如调用containsAll()和retainAll()方法）时，如果其参数也是EnumSet集合，则该批量操作的执行速度也非常快。
//3.EnumSet集合不允许加入null元素，如果试图插入null元素，EnumSet将抛出NullPointerException异常。
//4.EnumSet类没有暴露任何构造器来创建该类的实例，程序应该通过它提供的类方法来创建EnumSet对象。
//5.如果只是想判断EnumSet是否包含null元素或试图删除null元素都不会抛出异常，只是删除操作将返回false，因为没有任何null元素被删除。
//
//方法介绍：
//
//EnumSet allOf(Class elementType): 创建一个包含指定枚举类里所有枚举值的EnumSet集合。
//EnumSet complementOf(EnumSet e): 创建一个其元素类型与指定EnumSet里元素类型相同的EnumSet集合，新EnumSet集合包含
//        原EnumSet集合所不包含的、此类枚举类剩下的枚举值（即新EnumSet集合和原EnumSet集合的集合元素加起来是该枚举类的所有枚举值）。
//EnumSet copyOf(Collection c): 使用一个普通集合来创建EnumSet集合。
//EnumSet copyOf(EnumSet e): 创建一个指定EnumSet具有相同元素类型、相同集合元素的EnumSet集合。
//EnumSet noneOf(Class elementType): 创建一个元素类型为指定枚举类型的空EnumSet。
//EnumSet of(E first,E…rest): 创建一个包含一个或多个枚举值的EnumSet集合，传入的多个枚举值必须属于同一个枚举类。
//EnumSet range(E from,E to): 创建一个包含从from枚举值到to枚举值范围内所有枚举值的EnumSet集合。
//示例代码：
//
//package com.collection;

import java.util.EnumSet;
import java.util.HashSet;

//public class EnumSetTest {
public class 使用EnumSet {

    public static void main(String[] args) {
        //1.创建一个包含Session（枚举类）里所有枚举值的EnumSet集合  EnumSet allOf(Class elementType)
        EnumSet<Session> e1 = EnumSet.allOf(Session.class);
        System.out.println(e1);//[SPRING, SUMMER, FAIL, WINTER]
     
        System.out.println(e1.toArray()[2]);//FAIL

        //2.创建一个空EnumSet  EnumSet noneOf(Class elementType)
        EnumSet e2 = EnumSet.noneOf(Session.class);
        System.out.println(e2);//[]

        //3. add()空EnumSet集合中添加枚举元素
        e2.add(Session.SPRING);
        e2.add(Session.SUMMER);
        System.out.println(e2);//[SPRING, SUMMER]
        System.out.println(e2.toArray()[0]);//SPRING

        //4. 以指定枚举值创建EnumSet集合
        //EnumSet of(E first,E…rest): 创建一个包含一个或多个枚举值的EnumSet集合，传入的多个枚举值必须属于同一个枚举类。
        EnumSet e3 = EnumSet.of(Session.SPRING,Session.FAIL);
        System.out.println(e3);//[SPRING, FAIL]

        //5.创建一个包含从from枚举值到to枚举值范围内所有枚举值的EnumSet集合。
        EnumSet e4 = EnumSet.range(Session.SPRING,Session.FAIL);
        System.out.println(e4);//[SPRING, SUMMER, FAIL]

        //6.创建一个其元素类型与指定EnumSet里元素类型相同的EnumSet集合，
        //  新EnumSet集合包含原EnumSet集合所不包含的枚举值
      //EnumSet complementOf(EnumSet e): 创建一个其元素类型与指定EnumSet里元素类型相同的EnumSet集合，新EnumSet集合包含
//      原EnumSet集合所不包含的、此类枚举类剩下的枚举值（即新EnumSet集合和原EnumSet集合的集合元素加起来是该枚举类的所有枚举值）。
        EnumSet e5 = EnumSet.complementOf(e4);
        System.out.println(e5);//[WINTER]
        
      Collection c = new HashSet();
      c.clear();
      c.add(Session.SPRING);
      c.add(Session.FAIL);

      //EnumSet copyOf(Collection c): 使用一个普通集合来创建EnumSet集合。
      EnumSet e6 = EnumSet.copyOf(c);
      System.out.println(e6);//[SPRING, FAIL]
      System.out.println(c.toArray()[0]);//SPRING
    }
}

//创建一个枚举
enum Session{
    SPRING,
    SUMMER,
    FAIL,
    WINTER
}

//注意：
//
//除此之外还可以复制另一个EnumSet集合中的所有元素来创建新的EnumSet集合，或者复制另一个Collection集合中的所有元素来创建
//新的EnumSet集合。
//
//示例：
//
//Collection c = new HashSet();
//        c.clear();
//        c.add(Session.SPRING);
//        c.add(Session.FAIL);
//        EnumSet e6 = EnumSet.copyOf(c);
//        System.out.println(e6);//[SPRING, FAIL]
//
//注意：当复制Collection集合中所有元素来创建新的EnumSet集合时，要求Collection集合中的所有元素必须是同一个枚举类的枚举值。
//
//错误示例：
//
//c.add("Java");
//    e6 = EnumSet.copyOf(c);//java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Enum
//

//EnumSet 的使用
//
//《创建 EnumSet》
//为了创建一个EnumSet，我们必须先引入java.util.EnumSet ，和其他Set 的实现不一样，EnumSet 没有提供构造方法，这是因
//为EnumSet的实现是一个抽象类,所以我们必须使用EnumSet提供的静态方法来创建一个EnumSet
//
////创建一个具有指定元素类型的空EnumSet。
//EnumSet<E>  noneOf(Class<E> elementType)       
//
////创建一个指定元素类型并包含所有枚举值的EnumSet
//<E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType) //EnumSet e1 = EnumSet.allOf(Session.class);
//
//// 创建一个包括枚举值中指定范围元素的EnumSet
//<E extends Enum<E>> EnumSet<E> range(E from, E to)
//// 初始集合包括指定集合的补集
//<E extends Enum<E>> EnumSet<E> complementOf(EnumSet<E> s)
//// 创建一个包括参数中所有元素的EnumSet
//<E extends Enum<E>> EnumSet<E> of(E e)
//<E extends Enum<E>> EnumSet<E> of(E e1, E e2)
//<E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3)
//<E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4)
//<E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4, E e5)
//<E extends Enum<E>> EnumSet<E> of(E first, E... rest)
////创建一个包含参数容器中的所有元素的EnumSet
//<E extends Enum<E>> EnumSet<E> copyOf(EnumSet<E> s)
//<E extends Enum<E>> EnumSet<E> copyOf(Collection<E> c)

//1. 使用 allOf(Size)
//使用allof()方法创建一个包含指定枚举类型的全部枚举变量的EnumSet,所以和EnumMap 一样的是我们必须先要创建一个枚举类型
//enum Size {
//    SMALL, MEDIUM, LARGE, EXTRALARGE
//}

//@Test
//public void create(){
//    EnumSet sizes=EnumSet.allOf(Size.class);
//    System.out.println("EnumSet: " + sizes);
//}

//Output
//EnumSet: [SMALL, MEDIUM, LARGE, EXTRALARGE]

//Size.class  代表了我们的参数是我们创建的枚举类型
//
//2. 使用 noneOf(Class)
//使用 noneOf()方法创建一个空的枚举集合
//@Test
//public void create(){
//    EnumSet sizes2=EnumSet.noneOf(Size.class);
//    System.out.println("EnumSet: " + sizes2);
//}

//Output
//Empty EnumSet : []

//上面我们通过创建了一个空的枚举集合
//注意: 我们只能插入的Size的枚举变量到该集合，其他类型的变量时不行的，因为我们的Set的类型就是我们声明的枚举类型——Size
//
//3. 使用 range(e1, e2)
//使用 range() 方法创建一个 enum set 包含e1 和 e2 之间的枚举变量(包括e1 和 e2)
//@Test
//public void create(){
//    EnumSet sizes3=EnumSet.range(Size.MEDIUM,Size.EXTRALARGE);
//    System.out.println("EnumSet: " + sizes3);
//}

//Output
//EnumSet: [MEDIUM, LARGE, EXTRALARGE]

//这里我们就只包含了Size.MEDIUM,Size.EXTRALARGE之间的元素。
//4. 使用 of(e1...)
//使用 of() 方法创建包含指定枚举变量的EnumSet
//@Test
//public void create(){
//    EnumSet<Size> sizes4 = EnumSet.of(Size.SMALL, Size.LARGE);
//    System.out.println("EnumSet: " + sizes4);
//}

//Output
//EnumSet2: [SMALL, LARGE]

//创建方法总结(noneOf)
//noneOf 的实现细节
//上面我们只是演示了不同的创建方法的含义，但是并没有说一些细节东西，其实所有的创建方法都是使用调用了noneOf() 方法的，
//// allOf
//public static <E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType) {
//    EnumSet<E> result = noneOf(elementType);
//    result.addAll();
//    return result;
//}
//// range
//public static <E extends Enum<E>> EnumSet<E> range(E from, E to) {
//    if (from.compareTo(to) > 0)
//        throw new IllegalArgumentException(from + " > " + to);
//    EnumSet<E> result = noneOf(from.getDeclaringClass());
//    result.addRange(from, to);
//    return result;
//}
//// of
//public static <E extends Enum<E>> EnumSet<E> of(E e1 ... ) {
//    EnumSet<E> result = noneOf(e1.getDeclaringClass());
//    result.add(e1);
//    result.add(e2);
//    return result;
//}

//可以看出所有的方法都调用了noneOf 方法，而且前面我们说EnumSet 是一个抽象类,所以EnumSet没有给我们提供构造方法来创建对象，而是使
//用静态方法来创建对象，其实到这里我们应该可以猜到EnumSet应该有实现类的，不然它给我们返回什么对象呢？那我们就看一下noneOf里面到底
//发生了什么
///**
// * Creates an empty enum set with the specified element type.
// * 使用指定的枚举类型创建一个空的EnumSet
// * @param <E> The class of the elements in the set
// * @param elementType the class object of the element type for this enum set
// * @return An empty enum set of the specified type.
// * @throws NullPointerException if <tt>elementType</tt> is null
// */
//public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
//    Enum<?>[] universe = getUniverse(elementType);
//    if (universe == null)
//        throw new ClassCastException(elementType + " not an enum");
//
//    if (universe.length <= 64)
//        return new RegularEnumSet<>(elementType, universe);
//    else
//        return new JumboEnumSet<>(elementType, universe);
//}

//其实EnumSet有两个实现类RegularEnumSet、JumboEnumSet，当枚举类型的枚举变量小于等于64的时候使用RegularEnumSet，否则就
//使用JumboEnumSet,所以大多时候，我们用的都是RegularEnumSet。
//我们注意到上面在noneOf方法中都调用了 getUniverse(elementType),我们看一下这个方法都干了什么,注释信息说这个方法返回了枚举
//中的全部枚举常量，并且这个返回的结果会被缓存下来，然后被所有调用它的方法共享。
///**
// * Returns all of the values comprising E.
// * The result is uncloned, cached, and shared by all callers.
// */
//private static <E extends Enum<E>> E[] getUniverse(Class<E> elementType) {
//    return SharedSecrets.getJavaLangAccess()
//                                    .getEnumConstantsShared(elementType);
//}

//SharedSecrets 是一个比较底层的类了，我们可以不用太关注,只需知道它可以让我我们直接去访问类对象的一些信息即可，这里就是根据枚举类
//型的Class 信息获取到了全部的枚举常量。
//接下来我们再说一下，为什么上面的判断标准是64,而不是其他的数字呢
//class RegularEnumSet<E extends Enum<E>> extends EnumSet<E> {
//    private static final long serialVersionUID = 3411599620347842686L;
//    /**
//     * Bit vector representation of this set.  The 2^k bit indicates the
//     * presence of universe[k] in this set.
//     */
//    private long elements = 0L;
// }
//class JumboEnumSet<E extends Enum<E>> extends EnumSet<E> {
//    private static final long serialVersionUID = 334349849919042784L;
//
//    /**
//     * Bit vector representation of this set.  The ith bit of the jth
//     * element of this array represents the  presence of universe[64*j +i]
//     * in this set.
//     */
//    private long elements[];
//}

//RegularEnumSet使用的是一个long属性来存储，而JumboEnumSet是用一个long的数组来存储，这也是为什么和64做比较的原因，因为long只
//有64位，最多标识64个枚举值，多的只能用数组来存储，但是我们注意到这两个类我们是没有办法使用的，因为它不是public 类，也就是说只能在
//同一包下面访问。
//其他静态方法是如何通过noneOf返回含有元素的EnumSet
//我们还是回到这些静态方法上，我们这里就以of方法为例进行探索
//// of
//public static <E extends Enum<E>> EnumSet<E> of(E e1 ... ) {
//  	// 我们知道noneOf 返回的是一个空的EnumSet
//    EnumSet<E> result = noneOf(e1.getDeclaringClass());
//   // 然后发现是将特定的元素添加到了集合中去的，其他方法特使同理的
//    result.add(e1);
//    result.add(e2);
//    return result;
//}

//EnumSet 添加元素
//
//add() - 添加指定的枚举变量到EnumSet
//addAll() 添加指定集合中的元素到EnumSet
//
//@Test
//public void add() {
//    // Creating an EnumSet using allOf()
//    EnumSet<Size> sizes1 = EnumSet.allOf(Size.class);
//
//    // Creating an EnumSet using noneOf()
//    EnumSet<Size> sizes2 = EnumSet.noneOf(Size.class);
//
//    // Using add method
//    sizes2.add(Size.MEDIUM);
//    System.out.println("EnumSet Using add(): " + sizes2);
//
//    // Using addAll() method
//    sizes2.addAll(sizes1);
//    System.out.println("EnumSet Using addAll(): " + sizes2);
//}

//Output
//EnumSet using add(): [MEDIUM]
//EnumSet using addAll(): [SMALL, MEDIUM, LARGE, EXTRALARGE]

//这里我们就以add 方法为例说一下它是如何通过位向量实现添加操作的，其他方法同理
///**
// * Adds the specified element to this set if it is not already present.
// *
// * @param e element to be added to this set
// * @return <tt>true</tt> if the set changed as a result of the call
// *
// * @throws NullPointerException if <tt>e</tt> is null
// */
//public boolean add(E e) {
//    typeCheck(e);
//
//    long oldElements = elements;
//    elements |= (1L << ((Enum<?>)e).ordinal());
//    return elements != oldElements;
//}

//add操作实际是用位运算，将这个long值对应你传入的枚举值的下标的那个bit位改成1，比如我传入的是Size.MEDIUM, 对应的ordinal是1，则是将elements的bit位中的第2位改成1，
//EnumSet 获取元素
//为了获取EnumSet中的元素我们可以使用iterator() 方法
//@Test
//public void access() {
//    // Creating an EnumSet using allOf()
//    EnumSet<Size> sizes = EnumSet.allOf(Size.class);
//
//    Iterator<Size> iterate = sizes.iterator();
//
//    System.out.print("EnumSet: ");
//    while(iterate.hasNext()) {
//        System.out.print(iterate.next());
//        System.out.print(", ");
//    }
//}

//Output
//EnumSet: SMALL, MEDIUM, LARGE, EXTRALARGE,

//EnumSet 删除元素
//
//remove() - 删除指定元素
//removeAll() - 删除全部元素
//
//@Test
//public void remove() {
//    // Creating EnumSet using allOf()
//    EnumSet<Size> sizes = EnumSet.allOf(Size.class);
//    System.out.println("EnumSet: " + sizes);
//
//    // Using remove()
//    boolean value1 = sizes.remove(Size.MEDIUM);
//    System.out.println("Is MEDIUM removed? " + value1);
//    System.out.println(sizes);
//
//    // Using removeAll()
//    boolean value2 = sizes.removeAll(sizes);
//    System.out.println("Are all elements removed? " + value2);
//    System.out.println(sizes);
//
//}

//Output
//EnumSet: [SMALL, MEDIUM, LARGE, EXTRALARGE]
//Is MEDIUM removed? true
//[SMALL, LARGE, EXTRALARGE]
//Are all elements removed? true
//[]

//
//EnumSet 的其他方法

//Method         Description
//1.copyOf()     Creates a copy of the EnumSet
//2.contains()   Searches the EnumSet for the specified element and returns a boolean result
//3.isEmpty()    Checks if the EnumSet is empty
//4.size()       Returns the size of the EnumSet
//5.clear()      Removes all the elements from the EnumSet
//
//EnumSet的使用场景
//EnumSet 相比 HashSet, TreeSet 提供了一个更加高效的方式来存储枚举变量。
//一个EnumSet 对象只存储一个枚举类型的变量，因此JVM已经提前知道所有可能的元素，这也就是为什么EnumSet 内部使用的是一系列
//的Bits 来实现的，Bits标识了特定位置上的枚举变量是否存在
//EnumSet最有价值的是其内部实现原理，采用的是Bit 来实现的，它体现出来的是一种高效的数据处理方式，而且这种思想在很多地方都用
//的到，例如计算UV 或者在一些表示有没有或者是不是的场景中，比较有名的就是布隆过滤器
//备注 很多人也将其称为位向量，既然是向量那就可以计算。

//总结
//
//EnumSet 是一个用来存储枚举常量的集合，其底层是通过位向量实现的，所以有比较好的一个性能。
//其实EnumSet有两个实现类RegularEnumSet、JumboEnumSet，当枚举类型的枚举常量的数目等于64的时候使用RegularEnumSet，否则就
//使用JumboEnumSet,但是我们注意到这两个类我们是没有办法使用的，因为它是protected修饰的(省略了)，也就是说只能在同一包下面访问。
//这两个子类只是对于EnumSet的接口的实现方式不同，不同的地方就是根据调用方的Enum的值得个数来决定的，对于调用方来说是不需要关心这些
//细节的，所以这两个子类只需要是protected就可以，并且通过静态工厂类返回给调用方，以后如果有更牛逼的算法出现时，只需要再扩展出一个子
//类就可以了，而调用方是无感知的，比较符合开闭原则。
//所有的创建创建方法其实都是调用了noneOf 创建方法，先创建一个空的EnumSet(RegularEnumSet或者JumboEnumSet)，然后通过添加元
//素的方式将数据添加进去

